home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / PCASM2.ZIP / BAS2-1.DOC < prev    next >
Text File  |  1990-08-01  |  32KB  |  704 lines

  1.  
  2.  
  3.  
  4.                                                                          mmvii
  5.  
  6.                        BASIC II - INTERFACING BASIC WITH ASSEMBLER
  7.  
  8.  
  9.              Have you finished reading all the chapters? If not, go back and
  10.              do them, then come back to this when you are done. This chapter
  11.              assumes you know about segments, subroutines, and the general
  12.              information about linking subroutines to high-level languages.
  13.  
  14.              In order to do this appendix I had to dust off my old QuickBASIC
  15.              3.0. If you have QuickBASIC 4.x, some things will have been
  16.              updated. If you have TurboBASIC, the subroutine conventions are 
  17.              different. However, the structure will be the same. You will have
  18.              to consult your manual for exact details. If you are trying to
  19.              this with the interpreter that came with DOS, I have a simple
  20.              comment -> Forget it! I won't go into the details, but the BASIC
  21.              interpreter is so much slower (about 10 times slower) and so much
  22.              more difficult to use with assembler that I won't even cover it.
  23.              There is no reason not to use one of the compiled BASICs if BASIC
  24.              is your language of choice. This material only covers how to deal
  25.              with COMPILED BASIC.
  26.  
  27.  
  28.              In BASIC, all individual numeric data, strings, "static" arrays
  29.              and the stack must fit into one 64k segment. The word 'segment'
  30.              here has the same meaning as in assembler. Both the DS register
  31.              and the SS register are set to this segment, and must stay set to
  32.              this segment whenever BASIC has control of the program. "Dynamic"
  33.              arrays can be located somewhere else in memory. 
  34.  
  35.              You allocate a "static" array with a constant number as a
  36.              dimension:
  37.  
  38.                  DIM  array1! (277), array2% (346), array3$ (500)
  39.  
  40.              and you allocate a "dynamic" array by using a variable to
  41.              dimension the array:
  42.  
  43.                  length1% = 277
  44.                  length2% = 346
  45.                  length3$ = 500
  46.  
  47.                  DIM  array1!(length1%), array2%(length2%), array3$(length3%)
  48.  
  49.              Even though the first and second dimension statements produce the
  50.              same size and type arrays, the first ones must be located inside
  51.              DS and the second ones can be located outside of DS. 
  52.  
  53.              "Static" means that once the array is defined, its length and
  54.              number of dimensions cannot be changed for the rest of the
  55.              program. It will occupy a specific amount of space for the rest
  56.              of the program. "Dynamic" means that you can change the length of
  57.              the array whenever you want to. You do this with:
  58.  
  59.                  REDIM  array1! (495)
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                     mmviii
  69.              ______________________
  70.  
  71.  
  72.              BASIC does this by deallocating space for the old array and then
  73.              reallocating space for the new array. All the old information is
  74.              lost. There are certain restrictions. You cannot change the
  75.              number of dimensions in an array (if it starts out with 2
  76.              dimensions like DIM A!(47,63), it must always have 2 dimensions).
  77.  
  78.              In order to understand BASIC's memory strategy, we need to look
  79.              at strings, the reason for it all.{1} The limit for a single
  80.              string is 32,767 bytes. If the total amount of data you can have
  81.              in the DS segment is only 65536 bytes, how does BASIC allocate
  82.              memory so you can have long strings without runnung out of space?
  83.              It uses only as much space as it needs. Let's define 3 strings
  84.              (the dots will indicate a space):
  85.  
  86.                  mystring$ = "You.say.either"
  87.                  yourstring$ = "And.I.say.either"
  88.                  ourstring$ = "Let's.call.it.off"
  89.  
  90.              After defining these three strings one after the other, memory
  91.              will look like this:
  92.  
  93.                  17150
  94.                  |You.say.eitherAnd.I.say.eitherLet's.call.it.off|
  95.  
  96.              (For clarity, the memory image will be between the '|'s and each
  97.              row will be 50 bytes long. The next row down would start at
  98.              17200). For our example we will assume that this data starts at
  99.              memory location 17150. 
  100.  
  101.              There is no empty space. How does BASIC know where and how long
  102.              mystring$ is? It has something called a string descriptor. This
  103.              is a two word (4 byte) block, also in DS, which says exactly
  104.              where and how long the string is. The first word is the length
  105.              and the second word is the location (offset) in DS.
  106.  
  107.              From BASIC's view, we have:
  108.  
  109.                  STRING         DESCRIPTOR
  110.                               length:location
  111.  
  112.                  mystring$      14:17150       ->        |You.say.either|
  113.                  yourstring$    16:17164       ->        |And.I.say.either|
  114.                  ourstring$     17:17180       ->        |Let's.call.it.off|
  115.  
  116.              Now let's change one of the strings:
  117.  
  118.                  yourstring$ = "But.oh!,.If.we.call.the.whole.thing.off"
  119.  
  120.              We now have a problem. The current "yourstring$" is only 16 bytes
  121.              long, but the new one is 39 bytes long. What does BASIC do? It
  122.              (1) deallocates the space for the old "yourstring", (2) allocates
  123.              new space for the new string and (3) updates the string
  124.              ____________________
  125.  
  126.                 1. This is an outline of what BASIC does, but it will not
  127.              include the parts of memory management that you will never see.
  128.  
  129.  
  130.  
  131.  
  132.              BASIC II - Interfacing BASIC With Assembler                  mmix
  133.              ___________________________________________
  134.  
  135.              descriptor. Memory will now look like this:
  136.  
  137.                  17150
  138.                  |You.say.either                Let's.call.it.offBut|
  139.                  |.oh!,.If.we.call.the.whole.thing.off|
  140.  
  141.              and the descriptors will now look like this:
  142.  
  143.  
  144.                  STRING         DESCRIPTOR
  145.                               length:location
  146.  
  147.                  mystring$      14:17150       ->        |You.say.either|
  148.                  yourstring$    39:17197       ->        |But.oh!,.if.we...
  149.                  ourstring$     17:17180       ->        |Let's.call.it.off|
  150.  
  151.              BASIC is aware that there is an empty block of space and has a
  152.              strategy for dealing with empty spaces, though each BASIC has its
  153.              own strategy. We don't know exactly WHEN it will take action, but
  154.              we do know WHAT action it will take. At some point BASIC will
  155.              decide that it has too many empty spaces in memory and will
  156.              REORGANIZE the segment. This is known as GARBAGE COLLECTION.
  157.              Exactly how this is done is up to the person who wrote the BASIC
  158.              compiler/interpreter. 
  159.  
  160.              After reorganization, the addresses of ALMOST ALL strings and
  161.              MANY dynamic arrays will have changed. The string locations
  162.              themselves will have changed, but the string descriptors will
  163.              still be in the same place in DS, and they will have been
  164.              updated. Here is the new memory:
  165.  
  166.  
  167.                                          12724
  168.                                          |You.say.eitherLet's.call.i|
  169.                   |t.offBut.oh!,.If.we.call.the.whole.thing.off|
  170.  
  171.              and here are the updated descriptors:
  172.  
  173.                  STRING         DESCRIPTOR
  174.                               length:location
  175.  
  176.                  mystring$      14:12724       ->        |You.say.either|
  177.                  yourstring$    39:12755       ->        |But.oh!,.if.we...
  178.                  ourstring$     17:12738       ->        |Let's.call.it.off|
  179.  
  180.              The strings have been moved several thousand bytes from where
  181.              they were just a second ago. The information that was in the
  182.              string descriptors a second ago is no longer valid. Old
  183.              information about dynamic arrays is also unreliable. This means
  184.              that if you have a subroutine written in assembler, you must get
  185.              any address information at the time the subroutine is called.
  186.              We'll come back to this later.
  187.  
  188.  
  189.              Let's go on to data input and output. When you first started
  190.  
  191.  
  192.  
  193.  
  194.  
  195.              The PC Assembler Tutor                                        mmx
  196.              ______________________
  197.  
  198.              doing BASIC, you did i/o using only:
  199.  
  200.                  WRITE #1, my.data!
  201.  
  202.              Perhaps you you do it differently now, perhaps not. In any case,
  203.              you need to know about i/o speed and how different file i/o
  204.              works. Here's the simplest file output:
  205.  
  206.                  ***********************************
  207.                  DIM   large.array! (10000) 
  208.               
  209.                  FOR i% = 1 to 10000 
  210.                       large.array! (i%) = 2.1 
  211.                  NEXT i% 
  212.               
  213.                  OPEN "2-1.doc" for output as # 1 
  214.                  PRINT time$ 
  215.                  FOR i% = 1 to 10000 
  216.                       WRITE #1, large.array! (i%) 
  217.                  NEXT i% 
  218.                  PRINT time$ 
  219.                  CLOSE #1 
  220.                  ***********************************
  221.  
  222.              Of course, to make it a challenge we are going to write an array
  223.              of 10,000 numbers. How long does it take?{2} For this output it
  224.              took 38 seconds. The same program, inputting the same data with:
  225.  
  226.                  INPUT #1, large.array(i%)
  227.  
  228.              took 49 seconds. These are fairly large amounts of time. But
  229.              wait, it gets worse. Let's change one line of the above program:
  230.  
  231.                  large.array! (i%) = 2.1678319E+19
  232.  
  233.              This is a different constant which is put into each element of
  234.              the array. How long does output take now? 59 seconds. And input?
  235.              a whopping 79 seconds! What's going on here?
  236.  
  237.              When you do i/o with INPUT #, WRITE # or PRINT #, it is exactly
  238.              like doing i/o to the screen. For output, BASIC converts the
  239.              binary numbers into TEXT and then writes the TEXT to the disk.
  240.              When it does input, it reads the TEXT from the disk and converts
  241.              the TEXT into a binary number. Here is the beginning of the
  242.              output file from the first example:
  243.  
  244.                  2.1
  245.                  2.1
  246.                  2.1
  247.                  2.1
  248.              ____________________
  249.  
  250.                 2. All times from now on are with a slower PC with a slower
  251.              hard disk, but an 8087. Since these are floating point numbers,
  252.              your results should be slower if you don't have an 80x87, while
  253.              if you have an 80386 with an 80387 and a fast hard disk, your
  254.              times will be much faster.
  255.  
  256.  
  257.  
  258.  
  259.              BASIC II - Interfacing BASIC With Assembler                  mmxi
  260.              ___________________________________________
  261.  
  262.                  2.1
  263.  
  264.              Each data item has been converted into "2.1" + CHR$(13) +
  265.              CHR$(10). These last two things are a carriage return on the IBM
  266.              PC. That's (5 bytes X 10000 items) plus 1 byte for the end of
  267.              file marker, or 50001 bytes:
  268.  
  269.                  2-1      DOC    50001   6-29-90  12:39p 
  270.  
  271.              Here's the beginning of the output file from the second example:
  272.  
  273.                  2.167832E+19
  274.                  2.167832E+19
  275.                  2.167832E+19
  276.                  2.167832E+19
  277.                  2.167832E+19
  278.  
  279.              Each data item has been converted into "2.167832E+19" + CHR$(13)
  280.              + CHR$(10). That's (14 bytes * 10000 items) plus 1 byte for the
  281.              end of file marker, or 140001 bytes:
  282.  
  283.                  2-1E19   DOC   140001   6-29-90  12:47p 
  284.  
  285.              These files are unnecessarily large, and i/o is slow: if you
  286.              don't have an 8087 and you are doing floating-point i/o, it can
  287.              be slower still.
  288.  
  289.              Can we do it faster? Yes. Using GET and PUT, we get a certain
  290.              number of bytes from the disk, then transfer them to the array.
  291.              Some of you have never used random access i/o, so this is a brief
  292.              summary. 
  293.  
  294.              When you open a file as text (as we did in the above examples),
  295.              BASIC divides the text by looking for carriage returns. When you
  296.              open a file as a random access file, you are telling BASIC that
  297.              you want to divide the file into distinct blocks of information.
  298.              It may be text or it may be something else - BASIC doesn't care.
  299.              If you say nothing, BASIC assumes that you want the blocks to be
  300.              128 bytes long, but the length can be anything.
  301.  
  302.              In the example that we will do, we will use 1024 byte blocks
  303.              because that is exactly 2 disk sectors long, so the disk can read
  304.              information easily and efficiently. If we had a block length of 4
  305.              bytes, the disk would have to do 10000 disk writes; that would be
  306.              very slow and be hard on the disk. Here's how we open the file:
  307.  
  308.                  OPEN "packed.doc" for RANDOM as #1 LEN = 1024 
  309.  
  310.              This will be a random access file and the block length will be
  311.              1024 bytes. When you tell it to read or write, it will do it 1024
  312.              bytes at a time. That is getting faster. 
  313.  
  314.              Where is the block of data that it is going to write to disk?
  315.              Here life starts getting complicated, so I hope you have
  316.              understood everything that we have done so far. When you open a
  317.              file, BASIC assigns it a BUFFER. The buffer has a fixed length
  318.              (either 128 bytes or the length you have designated), and is
  319.  
  320.  
  321.  
  322.  
  323.              The PC Assembler Tutor                                      mmxii
  324.              ______________________
  325.  
  326.              located somewhere in the DS data segment along with the numbers
  327.              and strings. Like a string, it is relocatable. We need a way to
  328.              pin it down. The easy and nice way would be if it were an array
  329.              and we cound address it like an array:
  330.  
  331.                  buffer#1 (45) = 20
  332.  
  333.              We are not that lucky. The only thing you can do is overlay a
  334.              template on the buffer, and work from the template. This template
  335.              MUST be made up of strings. We make up the template with a FIELD
  336.              statement.
  337.  
  338.                  FIELD #1, 1024 AS out.string$
  339.  
  340.              The FIELD statement starts out with the file # followed by a list
  341.              of strings and the length of each string. 
  342.  
  343.                  FIELD #1, 100 AS string1$, 200 AS string2$, 300 AS string3$
  344.  
  345.              The total length of the strings may be shorter than the buffer,
  346.              but may not be longer than the buffer. What does the FIELD
  347.              statement do?  The first thing that it does is set the string
  348.              descriptor for all of these strings. Let's say that at the moment
  349.              file #1 buffer is at 46217:
  350.  
  351.                  STRING         DESCRIPTOR
  352.                               length:location
  353.  
  354.                  string1$       100:46217
  355.                  string2$       200:46317
  356.                  string3$       300:46517
  357.  
  358.              The first string starts at the first byte of the buffer. The
  359.              second string starts right where the first string ends and the
  360.              third string starts right where the second string ends. This is
  361.              true for any FIELD statement, no matter how many strings are
  362.              defined. Because of the way BASIC does memory management, if it
  363.              moves the buffer, it will also update these string descriptors to
  364.              point to the same relative places in the buffer. These string
  365.              descriptors are on auto pilot. 
  366.  
  367.              Suppose now that we have the following string:
  368.  
  369.                  "Let's get physical"
  370.  
  371.              and we want to write it to disk as string1$. All we need to do
  372.              is:
  373.  
  374.                  string1$ = "Let's get physical"
  375.  
  376.              Right? No, that's very, very, very wrong. What you have just done
  377.              is alter the string descriptor of string1$ to point to an
  378.              entirely different place in memory. The string descriptors are
  379.              now:
  380.  
  381.                  string1$        18:58902
  382.                  string2$       200:46317
  383.  
  384.  
  385.  
  386.  
  387.              BASIC II - Interfacing BASIC With Assembler                mmxiii
  388.              ___________________________________________
  389.  
  390.                  string3$       300:46517
  391.  
  392.              BASIC deallocated the space for string1, reallocated it somewhere
  393.              else in memory, and changed the file descriptor. Not only is
  394.              string1 in a different place in memory, but BASIC may think that
  395.              part of the file #1 buffer is actually empty space, and the next
  396.              time it reorganizes memory, who knows what is going to happen. 
  397.              From the moment you define strings in a FIELD statement until the
  398.              time you close the corresponding file, you can NEVER have them on
  399.              the left side of an equal sign. Having them on the left side is
  400.              sure to change the file descriptor.
  401.  
  402.              How are we going to transfer data to these strings? There are
  403.              three special operators in BASIC - LSET, MID$ and RSET. Their job
  404.              is to put something into a string without altering the string
  405.              length or location (i.e. without altering the string descriptor).
  406.  
  407.                  LSET string1$ = "Let's get physical"
  408.                  MID$ (string1$,17) = "Let's get physical"
  409.                  RSET string1$ = "Let's get physical"
  410.  
  411.              LSET will insert the string at the very left of string1, RSET
  412.              will insert the string at the very right of string1, and MID$
  413.              will insert the string starting at the 17th byte of string1.
  414.  
  415.              This is the strategy for all random access i/o in BASIC. We:
  416.  
  417.                  1) open a file as RANDOM and declare a block size.
  418.                  2) define some "fixed length" strings inside the buffer with
  419.                     a FIELD statement.
  420.                  3) insert data in the strings using LSET, RSET or MID$. This
  421.                     is true whether the data is strings or numbers.
  422.  
  423.              There's only one problem left. For LSET, RSET and MID$, the thing
  424.              on the RIGHT side of the equal sign must be a string. You can't
  425.              have:
  426.  
  427.                  LSET string1$ = number!
  428.  
  429.              It's illegal. To counter this, BASIC has some pseudo-functions.
  430.              Let's take integers as an example:
  431.  
  432.                  a.string$ = MKI$ (number%)
  433.                  number% = CVI (a.string$)
  434.  
  435.              MKI$ doesn't actually DO anything. It just tells BASIC that it is
  436.              o.k. to move two bytes from "number%" to "a.string$". The bytes
  437.              are binary data and are moved unaltered. Similarly, CVI tells
  438.              BASIC that it is alright to move two bytes of binary data from
  439.              "a.string$" to "number%". We are tricking BASIC into moving
  440.              binary data from one data type to another. This is simply data
  441.              movement, and there is no data conversion. The forms are:
  442.  
  443.                  NUMERIC DATA MOVE          TO STRING      FROM STRING
  444.  
  445.                  integer <-> string            MKI$           CVI
  446.                  long integer <->string        MKL$           CVL
  447.  
  448.  
  449.  
  450.  
  451.              The PC Assembler Tutor                                      mmxiv
  452.              ______________________
  453.  
  454.                  single precision <-> string   MKS$           CVS
  455.                  double precision <-> string   MKD$           CVD
  456.  
  457.              In contrast, the functions STR$ and VAL convert text
  458.              representations to binary representations and binary
  459.              representations to text representations. This is the same as what
  460.              happens with PRINT and INPUT. Here's a program:
  461.  
  462.                      **********************************************
  463.                      number! = 2.1678319E+19
  464.                      binary.string$ = MKS$ (number!)
  465.                      text.string$ = STR$ (number!)
  466.                      PRINT  LEN(text.string$), LEN(binary.string$)
  467.                      PRINT  text.string$, binary.string$
  468.                      **********************************************
  469.  
  470.              and here's the output:
  471.  
  472.                   13            4
  473.                   2.167832E+19 nl _
  474.  
  475.              You probably won't be able to see all of that last output on your
  476.              printer because it is four bytes long and the number is:
  477.  
  478.                  6E6C965F hex or   110, 108, 150, 95 decimal
  479.  
  480.              The third byte is outside of ASCII 33-127, the standard ASCII
  481.              characters. 
  482.  
  483.              STR$ gives us the text representation of the number, while MKS$
  484.              stuffs the binary representation of a number into a string. In
  485.              the opposite direction, VAL gives us the numeric value of a text
  486.              string (if it has a numeric representation), while CVS stuffs 4
  487.              binary bytes from a string into a single precision number.
  488.  
  489.                  STR$      from binary value to text representation
  490.                  VAL       from text representation to binary value
  491.  
  492.              Note that STR$ can convert ANY type of number to a text string
  493.              and VAL can convert a text string to ANY type of number, while
  494.              CVI, CVL, CVS, CVD, MKI$, MKL$, MKS$, and MKD$ can only stuff a
  495.              specific type of number into a string or a string into a specific
  496.              type of number.
  497.  
  498.              We want our output program to stuff the binary value from a
  499.              single precision number to selected bytes of a string. To stuff a
  500.              floating-point number into string1$ above, all we need to do is:
  501.  
  502.                  LSET string1 = MKS$ ( number!)
  503.  
  504.              The following program has a single string which is the size of
  505.              the entire buffer, and we are going to stuff the single precision
  506.              numbers in one at a time with MID$. 
  507.  
  508.                  ************************************************************
  509.                  number% = 10240 
  510.                  DIM   large.array! (number%) 
  511.  
  512.  
  513.  
  514.  
  515.              BASIC II - Interfacing BASIC With Assembler                  mmxv
  516.              ___________________________________________
  517.  
  518.               
  519.                  FOR i% = 1 to 10240 
  520.                       large.array! (i%) = 2.1678319e+19 
  521.                  NEXT i% 
  522.               
  523.                  OPEN "packed.doc" for RANDOM as #1 LEN = 1024 
  524.                  FIELD #1 , 1024 AS out.string$ 
  525.               
  526.                  PRINT time$ 
  527.                  k% = 0 
  528.                  record.count% = 0  
  529.                  FOR i% = 1 to 40 
  530.                       record.count% = record.count% + 1 
  531.                       spot% = 1 
  532.                       FOR j% = 1 to 256  
  533.                          k% = k% + 1 
  534.                          MID$ (out.string$,spot%,4) = MKS$ (large.array!(k%))
  535.                          spot% = spot% + 4 
  536.                       NEXT j% 
  537.                       PUT #1, record.count%  
  538.                  NEXT i%  
  539.                  PRINT time$  
  540.                  CLOSE #1  
  541.                  ***********************************************************
  542.  
  543.  
  544.              The array length has been increased slightly so that we have an
  545.              exact number of blocks. We use MID$ to make sure that the string
  546.              descriptor for out.string$ does not get changed. Each file write
  547.              will be (256 numbers * 4 bytes/number) 1024 bytes long. We start
  548.              with the first record and increase the record number by 1 each
  549.              time we write. Does this increase the speed any? Well, this takes
  550.              11 seconds.
  551.  
  552.                  TYPE                     OUTPUT              INPUT
  553.  
  554.                  num <-> text             38 - 59 sec         49 - 79 secs
  555.                  num <-> bin. string      11 sec              11 sec
  556.  
  557.              I didn't show you the equivalent input routines but here are the
  558.              times they took. Note that the complexity of the single precision
  559.              number has no effect on the last (the binary) routine. Also, the
  560.              last routine does not suffer if there is no 8087. If you are
  561.              running an 80286 with a fast hard disk, this last routine should
  562.              only take a second or two. Here are the file sizes:
  563.  
  564.                  2-1      DOC    50001   6-29-90  12:39p 
  565.                  2-1E19   DOC   140001   6-29-90  12:47p 
  566.                  PACKED   DOC    40960   6-29-90   1:08p 
  567.  
  568.              The first two are the different sizes depending on whether the
  569.              constant was 2.1 or 2.1678319E+19. The last one is for our last
  570.              routine. Notice that it is more compact.
  571.  
  572.              Can we do any better than 11 seconds? Yes, but we need to take
  573.              over disk i/o and we need to know a few more things before we do
  574.              that.
  575.  
  576.  
  577.  
  578.  
  579.              The PC Assembler Tutor                                      mmxvi
  580.              ______________________
  581.  
  582.  
  583.  
  584.              LOCATION OF DATA
  585.  
  586.              BASIC is designed to pass subroutines the location of the data,
  587.              not the data itself. This is called passing by reference. Though
  588.              it is possible to pass the data itself, there are certain
  589.              problems with the stack if you do.{3} We will always pass the
  590.              addresses.
  591.  
  592.              All single numeric variables are in the DS segment. BASIC passes
  593.              the offset address of these variables in DS (1 word). 
  594.  
  595.              All strings are in the DS segment. Their string descriptors are
  596.              also in the DS segment. BASIC always passes the offset address of
  597.              the STRING DESCRIPTOR. This, in fact, is what we want. We need to
  598.              know both where the string is and how long it is. If we write
  599.              past the end of the string we may destroy BASIC's memory
  600.              management system.
  601.  
  602.              Static arrays are in the DS segment but dynamic arrays can be
  603.              anywhere. If we want to write a general purpose routine with
  604.              arrays, we need to handle them no matter where they are.
  605.  
  606.              BASIC has a special function called VARPTR that tells you where a
  607.              variable is in memory. Here's a program that uses it for a couple
  608.              of variables:
  609.  
  610.                  ***********************************************************
  611.                  ' check out the use of varptr 
  612.                  n% = 5000 
  613.                  p% = 50 
  614.                  DIM  b!(800),a!(900) 
  615.                  DIM  d!(n%), c!(p%) 
  616.               
  617.                  mystring$ = "What's up, doc?" 
  618.                  addressA! = varptr (n%) 
  619.                  addressB! = varptr (p%) 
  620.                  address1! = varptr (a!(0)) 
  621.                  address2! = varptr (b!(0)) 
  622.                  address3! = varptr (c!(0)) 
  623.                  address4! = varptr (d!(0)) 
  624.                  address5! = varptr (mystring$) 
  625.                  PRINT addressA!, addressB! 
  626.                  PRINT address1!, address2!, address3!, address4!, address5! 
  627.                  ***********************************************************
  628.  
  629.              It gives us the addresses of all sorts of things. a!() and b!()
  630.              are static arrays, so they should be in the DS segment. c!() and
  631.              d!() are dynamic arrays, so they might be anywhere. Remember, the
  632.              DS segment is from offset 0 to offset 65535. Let's see where they
  633.              ____________________
  634.  
  635.                 3. If you make a mistake and pass a single precision number
  636.              instead of an integer, you will pass 4 bytes instead of 2. From
  637.              that moment on the stack will have 2 extra bytes on it and you
  638.              won't know where they came from.
  639.  
  640.  
  641.  
  642.  
  643.              BASIC II - Interfacing BASIC With Assembler                mmxvii
  644.              ___________________________________________
  645.  
  646.              are:
  647.  
  648.                   6230         6232 
  649.                   9438         6234         87616        67584        13062 
  650.  
  651.              The individual numbers are in DS, and the two static arrays are
  652.              in DS, but c!() and d!() are outside of DS. These numbers tell us
  653.              the address relative to the start of DS, but we don't know where
  654.              DS is at the moment. Where exactly are these arrays? It would be
  655.              tedious to pass the subroutine these numbers because they are
  656.              floating-point numbers and would be very difficult to deal with. 
  657.  
  658.              QuickBASIC has a function called PTR86. It is in an external
  659.              object file called INT86.OBJ.{4} This object file has the
  660.              routines that you need if you want to do interrupts from BASIC
  661.              itself. We'll come back to that. PTR86's job is to take the
  662.              floating-point number which we got from VARPTR, add the starting
  663.              address of the DS segment to get an absolute address in memory, 
  664.              and then calculate both a segment and an offset for that address
  665.              in memory. The segment will always be the highest segment that
  666.              contains the first byte of the variable or array and the offset
  667.              will always be a number from 0 to 15.
  668.  
  669.              In order to use an object file from inside of QuickBASIC you need
  670.              to put it in a library file and then load the library file when
  671.              starting QuickBASIC.
  672.  
  673.              Building the library file is quite easy. QuickBASIC comes with a
  674.              program called BUILDLIB.EXE which builds the library for you. For
  675.              now, you need only INT86.OBJ and PREFIX.OBJ in your library.{5}
  676.              Put these two things in every library that you build from now on.
  677.              PREFIX.OBJ insures proper segment ordering in the executable
  678.              file. 
  679.  
  680.                  >buildlib  int86+prefix
  681.  
  682.              This will create a library with the default name USERLIB.EXE. To
  683.              load a library with this default file name, just put '/l' on the
  684.              command line:
  685.  
  686.                  >qb  /l
  687.  
  688.              If you have given the library a different name like XQRTYF.EXE,
  689.              then put that name after the '/l':
  690.  
  691.                  >qb /lXQRTYF.EXE
  692.  
  693.              These object files will now be loaded and their subroutines will
  694.              be usable from inside BASIC.
  695.  
  696.  
  697.  
  698.              ____________________
  699.  
  700.                 4. PTR86 has been replaced by VARSEG in QuickBASIC 4.0.
  701.  
  702.                 5. Both of these object files come with your QuickBASIC.
  703.  
  704.